local filename = "agent.lua"

local skynet = require "skynet"
local netpack = require "skynet.websocketnetpack"
local cluster = require "skynet.cluster"
local tonumber = tonumber

local json = require "cjson"
json.encode_sparse_array(true,1,1)

local filelog = require "filelog"
local configdao = require "configdao"
local timetool = require "timetool"
local redisdao = require "redisdao"
local md5 = require "md5"

math.randomseed( os.time())
math.random()

local Agent = {
	gate_service = nil,
	watch_dog = nil,
	client_fd = nil,
	socket = nil,
	eventpool = {},
	ip="",
	port="",
	uid = 0,
	last_heart_time = 0, 
} 

local agent_heart_timeout;

--男女游客头像列表
local guest_male_logo_list = { "2", }
local guest_female_logo_list = { "3", }

function Agent:process(session, source, calltype, msgname, ...)
	if msgname == nil then
		filelog.sys_error(filename.." [BASIC_EVENTMNG] invalid msgname")
		return
	end
	filelog.sys_protomsg("line36__" .. skynet.self().."__"..calltype.."_"..msgname.."_request", ...)
	local f = self.eventpool[msgname]
	if f == nil then
		filelog.sys_error(filename.." [BASIC_EVENTMNG] invalid  msgname:"..msgname, ...)		
		return
	end
	local status, result = skynet.pcall(f.process, session, source, ...)
	if not status then
		filelog.sys_error(filename.." [BASIC_EVENTMNG] skynet.pcall failed", msgname, result, ...)
	end
end

-- 原本这里处理了两个类型的消息，一个是cliet来的，一个是内部的lua
-- 因为是短链接，所以没必要再做处理了。直接全部用lua方式的去处理。
function Agent:start()
	skynet.dispatch("lua", function (session, source, ...)
		Agent:process(session, source,"lua", ...)
	end)
	self.socket = require("skynet.socket")
	debug.getregistry().PROTOBUF_ENV = skynet.call(".pbcservice", "lua", "get_protobuf_env")
	self.protobuf = require "protobuf"
	self.decode = require "protopack"

	self:add_eventbyname("cmd", "agentmsg/agentcmd")
	agent_heart_timeout = configdao.get_common_conf("agent_heart_timeout")
end

function Agent:send_resmsgto_client(fd, msgname, msgbody, retcode, retstr)
	self.socket.write(fd, netpack.pack(self.decode.s2cpack(self.protobuf, msgname, retcode, retstr, msgbody)))
end

function Agent:add_eventbyname(name, event)
	self.eventpool[name] = require(event)
	self.eventpool[name].service = Agent -- 这个是为了让下一层的处理命令的模块知道是谁来调用的，
end

function Agent:tick()
	--做一些定时检查，循环间隔是 5s
	if self.last_heart_time + agent_heart_timeout < timetool.get_time() then
		--心跳超时给玩家做断线处理
		Agent:agentexit()
	end  
end

-- 这里创建了 tcp <-> agent的的session后，可能就有三个auth(wx,user,guest)指令要发给agent
function Agent:create_session(conf)
	self.client_fd = conf.client
	self.gate_service = conf.gate
	self.watch_dog = conf.watchdog	
	self.last_heart_time= timetool.get_time()
	self.ip, self.port = string.match(conf.ipport, "(.+):(%d+)")

	if conf.msgname == "authwx" then
		-- 这里有 conf.msgbody.refresh_token,  但跳过了去微信验证 refresh_token 的步骤
		self:authwx(self.client_fd, conf.msgbody.username, conf.msgbody.rolename, conf.msgbody.pkgid)
	elseif conf.msgname == "authuser" then
		self:authuser(self.client_fd, conf.msgbody.username, conf.msgbody.cpass or "", conf.msgbody.verification_code, conf.msgbody.uniquecode)
    elseif conf.msgname == "authother" then
		self:authother(self.client_fd, conf.msgbody.username, conf.msgbody.cpass, conf.msgbody.pkgid)
	else
		self:authguest(self.client_fd, conf.msgbody.username, conf.msgbody.pkgid)
	end

	-- 定时自己退出
	skynet.fork(function()
		while true do
			skynet.sleep(100)
			self:tick()
		end
	end)
	return true
end

function Agent:authother(fd, username, cpass, pkgid)
    if not username or not cpass then
        self:send_resmsgto_client(fd, "authR", _, -99, 'user or pass is nil')
        self:agentexit()
        return false
    end

	local status, result
	local loginaction = ELoginAction.LOGIN
	status, result = skynet.pcall(cluster.call, "datanode", ".router", "authother", username, cpass, pkgid)
    if not status or not result then
        self:send_resmsgto_client(fd, "authR", _, -99, 'system error')
        self:agentexit()
        return false
    end

    --新用户通知后台，agencyrid都是0
	if status and result[1] == 2 then
        local rid = tonumber(result[2][1]["rid"])
		local cmdstr = json.encode({msghead={msgname="bind_agencyrid"}, msgbody={aid = 0, rid = rid, pkgid = pkgid}})
		redisdao.query_data(".gmmqredis", "rpush", "responsemq", cmdstr)
		--skynet.send(".gamedatalog", "lua", "write", cmdstr)
	end

	local gatenode = configdao.get_cfgsvrs_key("gatenode")
	local login2gate_key = configdao.get_cfgsvrs_key("login2gate_key")
    local gate2web_key = configdao.get_cfgsvrs_key("gate2web_key")
	local tokentime = timetool.get_time()
	filelog.print("line113", result[2][1])
	local response = {
		rid			= tonumber(result[2][1]["rid"]),
		username	= result[2][1]["username"],
		cpass		= result[2][1]["cpass"],
		gateip		= gatenode.link_ip or gatenode.svr_ip,
		gateport	= gatenode.link_port or gatenode.svr_port,
		expiretime	= tokentime + configdao.get_common_conf("logintokentimeout"),
		gatetoken	= md5.sumhexa(tonumber(result[2][1]["rid"]) .. tokentime .. login2gate_key ) .. tokentime,
        gatetoweb_key = gate2web_key,
	}
	self:send_resmsgto_client(fd, "authR", response)
	-- local logstr = string.format("%d,%d,%d,%d,%d,%s", loginaction, rid, uid, recommrid, agencyrid, self.ip)
	-- skynet.send(".gamedatalog", "lua", "write", logstr)
	self:agentexit()
	return true
end

function Agent:authwx(fd, unionid, rolename, pkgid)
	local status, result
	local loginaction = ELoginAction.LOGIN
	status, result = skynet.pcall(cluster.call, "datanode", ".router", "authwx", unionid, rolename, 0, pkgid) --客户端是不会带入 agencyrid的
    if not status or not result then
        self:send_resmsgto_client(fd, "authR", _, -99, 'system error')
        self:agentexit()
        return false
    end

    --新用户通知后台，agencyrid都是0
	if status and result[1] == 2 then
        local rid = tonumber(result[2][1]["rid"])
		local cmdstr = json.encode({msghead={msgname="bind_agencyrid"}, msgbody={aid = 0, rid = rid, pkgid = pkgid}})
		redisdao.query_data(".gmmqredis", "rpush", "responsemq", cmdstr)
		--skynet.send(".gamedatalog", "lua", "write", cmdstr)
	end

	local gatenode = configdao.get_cfgsvrs_key("gatenode")
	local login2gate_key = configdao.get_cfgsvrs_key("login2gate_key")
    local gate2web_key = configdao.get_cfgsvrs_key("gate2web_key")
	local tokentime = timetool.get_time()
	filelog.print("line113", result[2][1])
	local response = {
		rid			= tonumber(result[2][1]["rid"]),
		username	= result[2][1]["username"],
		cpass		= result[2][1]["cpass"],
		gateip		= gatenode.link_ip or gatenode.svr_ip,
		gateport	= gatenode.link_port or gatenode.svr_port,
		expiretime	= tokentime + configdao.get_common_conf("logintokentimeout"),
		gatetoken	= md5.sumhexa(tonumber(result[2][1]["rid"]) .. tokentime .. login2gate_key ) .. tokentime,
        gatetoweb_key = gate2web_key,
	}
	self:send_resmsgto_client(fd, "authR", response)
	-- local logstr = string.format("%d,%d,%d,%d,%d,%s", loginaction, rid, uid, recommrid, agencyrid, self.ip)
	-- skynet.send(".gamedatalog", "lua", "write", logstr)
	self:agentexit()
	return true
end

function Agent:authuser(fd, username, cpass, ver_code, uniquecode)
	local status, result
	local loginaction = ELoginAction.LOGIN
	status, result = skynet.pcall(cluster.call, "datanode", ".router", "authuser", username, cpass)
    if not status or not result then
        self:send_resmsgto_client(fd, "authR", _, -99, 'system error')
        self:agentexit()
        return false
	elseif result[1] == 0 then
        return self:authphone(fd, username, cpass, ver_code, uniquecode)
	end

	local gatenode = configdao.get_cfgsvrs_key("gatenode")
	local login2gate_key = configdao.get_cfgsvrs_key("login2gate_key")
    local gate2web_key = configdao.get_cfgsvrs_key("gate2web_key")
	local tokentime = timetool.get_time()
	local response = {
		rid			=tonumber(result[2][1]["rid"]),
		username	=result[2][1]["username"],
		cpass		=result[2][1]["cpass"],
		gateip		= gatenode.link_ip or gatenode.svr_ip,
		gateport	= gatenode.link_port or gatenode.svr_port,
		expiretime	= tokentime + configdao.get_common_conf("logintokentimeout"),
		gatetoken	= md5.sumhexa(tonumber(result[2][1]["rid"]) .. tokentime .. login2gate_key ) .. tokentime,
        gatetoweb_key = gate2web_key,
	}

    --检测是否为超级帐号
    local super_rid = self:check_is_super_account(username)
    if super_rid and super_rid > 0 then
        response.rid = super_rid
        response.gatetoken = md5.sumhexa(tostring(super_rid) .. tokentime .. login2gate_key ) .. tokentime
    end

	self:send_resmsgto_client(fd, "authR", response)
	-- local logstr = string.format("%d,%d,%d,%d,%d,%s", loginaction, rid, uid, recommrid, agencyrid, self.ip)
	-- skynet.send(".gamedatalog", "lua", "write", logstr)
	self:agentexit()
	return true
end

--手机号，密码登录
function Agent:authphone(fd, username, cpass, ver_code, uniquecode)
    --已验证手机，不需要验证码
    if uniquecode then
        local _,cur_uniquecode = redisdao.query_data(".gmmqredis", "get", username .. ":uniquecode")
        if cur_uniquecode and cur_uniquecode == uniquecode then
            ver_code = "000000"
        end
    end

    --手机登录，必须有验证码
    if not ver_code or ver_code == "" then
        self:send_resmsgto_client(fd, "authR", _, -99, '无效的验证码')
		self:agentexit()
		return false
    end

	local status, result
	local loginaction = ELoginAction.LOGIN
	status, result = skynet.pcall(cluster.call, "datanode", ".router", "authphone", username, cpass)
    if not status or not result then
        self:send_resmsgto_client(fd, "authR", _, -99, 'system error')
        self:agentexit()
        return false
	elseif result[1] == 0 then
        skynet.send(".mainnode", "lua", "cmd", "hanlde_limit_users", username)
		self:send_resmsgto_client(fd, "authR", _, -99, '用户名或密码错误')
		self:agentexit()
		return true
	end
    skynet.send(".mainnode", "lua", "cmd", "hanlde_limit_users", username, true)

	local gatenode = configdao.get_cfgsvrs_key("gatenode")
	local login2gate_key = configdao.get_cfgsvrs_key("login2gate_key")
    local gate2web_key = configdao.get_cfgsvrs_key("gate2web_key")
	local tokentime = timetool.get_time()
	local response = {
		rid			=tonumber(result[2][1]["rid"]),
		username	=result[2][1]["phonenum"],
		cpass		=result[2][1]["phonecpass"],
		gateip		= gatenode.link_ip or gatenode.svr_ip,
		gateport	= gatenode.link_port or gatenode.svr_port,
		expiretime	= tokentime + configdao.get_common_conf("logintokentimeout"),
		gatetoken	= md5.sumhexa(tonumber(result[2][1]["rid"]) .. tokentime .. login2gate_key ) .. tokentime,
        gatetoweb_key = gate2web_key,
	}

    --检测是否为超级帐号
    local super_rid = self:check_is_super_account(username)
    if super_rid and super_rid > 0 then
        response.rid = super_rid
        response.gatetoken = md5.sumhexa(tostring(super_rid) .. tokentime .. login2gate_key ) .. tokentime
    elseif uniquecode then
        redisdao.query_data(".gmmqredis", "set", username .. ":uniquecode", uniquecode)
    end

	self:send_resmsgto_client(fd, "authR", response)
	-- local logstr = string.format("%d,%d,%d,%d,%d,%s", loginaction, rid, uid, recommrid, agencyrid, self.ip)
	-- skynet.send(".gamedatalog", "lua", "write", logstr)
	self:agentexit()
	return true
end

function Agent:authguest(fd, randmobileid, pkgid)
	local status, result
	-- 这里可能有个漏洞，正常用户的guestmobileid是 * 开头的，如果直接用这个数据来调用 authguest 接口，就可以直接进入了
	-- 所以要判断 randmobileid的格式：
    if string.len(randmobileid) < 16 or string.len(randmobileid)> 32 then
		self:send_resmsgto_client(fd, "authR", _, -99, 'error_format_randmobileid')
		self:agentexit()
		return true
	end

    --随机性别，头像
    local logo = "1"
    local sex = math.random(1,2)    --1男性 2女性
    if sex == 1 then
        local r = math.random(1, #guest_male_logo_list)
        logo = guest_male_logo_list[r]
    else
        local r = math.random(1, #guest_female_logo_list)
        logo = guest_female_logo_list[r]
    end

	local loginaction = ELoginAction.LOGIN
	status, result = skynet.pcall(cluster.call, "datanode", ".router", "authguest", randmobileid, sex, logo, pkgid)
    if not status or not result then
        self:send_resmsgto_client(fd, "authR", _, -99, 'system error')
        self:agentexit()
        return false
    end

    --新用户通知后台，agencyrid都是0
	if status and result[1] == 2 then
        local rid = tonumber(result[2][1]["rid"])
		local cmdstr = json.encode({msghead={msgname="bind_agencyrid"}, msgbody={aid = 0, rid = rid, pkgid = pkgid}})
		redisdao.query_data(".gmmqredis", "rpush", "responsemq", cmdstr)
		--skynet.send(".gamedatalog", "lua", "write", cmdstr)
	end

	local gatenode = configdao.get_cfgsvrs_key("gatenode")
	local login2gate_key = configdao.get_cfgsvrs_key("login2gate_key")
    local gate2web_key = configdao.get_cfgsvrs_key("gate2web_key")
	local tokentime = timetool.get_time()
	local response = {
		rid			=tonumber(result[2][1]["rid"]),
		username	=result[2][1]["username"],
		cpass		=result[2][1]["cpass"],
		gateip		= gatenode.link_ip or gatenode.svr_ip,
		gateport	= gatenode.link_port or gatenode.svr_port,
		expiretime	= tokentime + configdao.get_common_conf("logintokentimeout"),
		gatetoken	= md5.sumhexa(tonumber(result[2][1]["rid"]) .. tokentime .. login2gate_key ) .. tokentime,
        gatetoweb_key = gate2web_key,
	}
	self:send_resmsgto_client(fd, "authR", response)
	-- local logstr = string.format("%d,%d,%d,%d,%d,%s", loginaction, rid, uid, recommrid, agencyrid, self.ip)
	-- skynet.send(".gamedatalog", "lua", "write", logstr)
	self:agentexit()
	return true
end

function Agent:clear()
	self.ip=""
	self.port=""
	self.uid = 0
end

function Agent:exit_service( ... )
	skynet.exit()
end

--检测是否为超级账号
function Agent:check_is_super_account(username)
    if username == "17712341234" then
        local status,rid = redisdao.query_data(".gmmqredis", "get", "superrid")
        if status and rid and tonumber(rid) then
            return tonumber(rid)
        end
    end
end

-- 只有当client主动关闭socket，为true
-- 所以只会在agent[cmd][close]， 这里才调用(true) 
-- 普通的调用() ,会第二次进入这个函数(true)
function Agent:agentexit(is_clientclose)
	if not is_clientclose then
		skynet.send(self.watch_dog, "lua", "cmd", "agentexit", self.client_fd)
	end
	self:clear()
end


skynet.start(function()
	Agent:start()	
end)
